/*
 * Copyright (c) 2022-2023 Termony Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "los_context.h"
#include "securec.h"
#include "los_arch_context.h"
#include "los_arch_interrupt.h"
#include "los_task.h"
#include "los_sched.h"
#include "los_interrupt.h"
#include "los_debug.h"

#define PSR_T_ARM           (0x00000000UL)
#define PSR_T_THUMB         (0x00000020UL)
#define PSR_MODE_SYS        (0x0000001FUL)

#define PSR_MODE_SYS_ARM    (PSR_MODE_SYS | PSR_T_ARM)
#define PSR_MODE_SYS_THUMB  (PSR_MODE_SYS | PSR_T_THUMB)


/* FPU上下文标识, 在任务切换时如果需要保存置1. */
volatile UINTPTR g_taskHasFPUContext = 0;
/* 任务调度请求标识, 在中断中请求调度任务时置1. */
volatile UINTPTR g_taskYieldRequired = 0;


/**
 * @brief  处理器底层初始化.
 * @note   该接口在LiteOS初始化时被调用.
 */
LITE_OS_SEC_TEXT_INIT VOID ArchInit(VOID)
{
    HalHwiInit();
}

/**
 * @brief  任务退出函数.
 * @note   该接口在任务退出时被调用.
 */
LITE_OS_SEC_TEXT_MINOR NORETURN VOID ArchSysExit(VOID)
{
    (VOID)LOS_IntLock();
    while (1) {}
}

/**
 * @brief  任务栈初始化.
 * @param  taskID 任务ID.
 * @param  stackSize 任务栈大小.
 * @param  topStack 指向任务栈栈顶的指针.
 * @note   该接口在任务创建时被调用.
 */
LITE_OS_SEC_TEXT_INIT VOID *ArchTskStackInit(UINT32 taskID, UINT32 stackSize, VOID *topStack)
{
    TaskContext *context = (TaskContext *)((UINTPTR)topStack + stackSize - sizeof(TaskContext));

    if ((UINTPTR)OsTaskEntry & 0x01) {
        /* System mode, Thumb mode, IRQ enabled FIQ enabled. */
        context->spsr = PSR_MODE_SYS_THUMB;
    } else {
        /* System mode, ARM mode, IRQ enabled FIQ enabled. */
        context->spsr = PSR_MODE_SYS_ARM;
    }
    /* Next the return address, which in this case is the start of the task. */
    context->pc = (UINTPTR)OsTaskEntry;
    context->lr = (UINTPTR)ArchSysExit; /* Exception return address. */
    context->sp = (UINTPTR)topStack + stackSize;
    /* Next all the registers other than the stack pointer. */
    context->r12 = 0x12121212UL; /* R12 */
    context->r11 = 0x11111111UL; /* R11 */
    context->r10 = 0x10101010UL; /* R10 */
    context->r9 = 0x09090909UL; /* R9 */
    context->r8 = 0x08080808UL; /* R8 */
    context->r7 = 0x07070707UL; /* R7 */
    context->r6 = 0x06060606UL; /* R6 */
    context->r5 = 0x05050505UL; /* R5 */
    context->r4 = 0x04040404UL; /* R4 */
    context->r3 = 0x03030303UL; /* R3 */
    context->r2 = 0x02020202UL; /* R2 */
    context->r1 = 0x01010101UL; /* R1 */
    context->r0 = (UINTPTR)taskID; /* R0 */

#if (LOSCFG_PLATFORM_FPU_ENABLE > 0)
    context->fpscr = 0;
    memset_s(context->q, sizeof(context->q), 0, sizeof(context->q));
#endif /* LOSCFG_PLATFORM_FPU_ENABLE */

    return (VOID *)context;
}

#if (LOSCFG_KERNEL_SIGNAL == 1)
VOID *ArchSignalContextInit(VOID *stackPointer, VOID *stackTop, UINTPTR sigHandler, UINT32 param)
{
    UNUSED(stackTop);
    TaskContext *context = (TaskContext *)((UINTPTR)stackPointer - sizeof(TaskContext));
    (VOID)memset_s((VOID *)context, sizeof(TaskContext), 0, sizeof(TaskContext));

    if ((UINTPTR)sigHandler & 0x01) {
        /* System mode, Thumb mode, IRQ enabled FIQ enabled. */
        context->spsr = PSR_MODE_SYS_THUMB;
    } else {
        /* System mode, ARM mode, IRQ enabled FIQ enabled. */
        context->spsr = PSR_MODE_SYS_ARM;
    }
    context->pc = (UINTPTR)sigHandler;
    context->r0 = (UINTPTR)param;

    return (VOID *)context;
}
#endif

/**
 * @brief  任务调度请求.
 * @note   该接口在需要任务调度时被调用.
 */
LITE_OS_SEC_TEXT_INIT VOID ArchTaskSchedule(VOID)
{
    if (g_intCount > 0) {
        g_taskYieldRequired = 1;
    } else {
        __asm__ __volatile__("SWI 0");
    }
}

/**
 * @brief  任务调度启动.
 * @note   该接口在LiteOS启动时被调用.
 */
LITE_OS_SEC_TEXT_INIT UINT32 ArchStartSchedule(VOID)
{
    (VOID)LOS_IntLock();
    OsSchedStart();
    HalStartToRun();
    return LOS_OK; /* never return */
}
